﻿#pragma once

#include "upacker.h"
#include "updater.h"

#include <QQuickItem>
#include <QSerialPort>
#include <QString>

// namespace qmlserialport {

/**
 * @brief QML wrapper for QSerialPort
 */
class SerialPort : public QObject {
  /* *INDENT-OFF* */
  Q_OBJECT
  /* *INDENT-ON* */

  /** @brief Name of the serial port, under windows it could be e.g "COM4",
   * under linux it could be e.g "/dev/ttyUSB0" */
  Q_PROPERTY(
      QString portName READ portName WRITE setPortName NOTIFY portNameChanged)

  Q_PROPERTY(QString deviceName READ deviceName WRITE setDeviceName NOTIFY
                 deviceNameChanged)

  /** @brief Communication speed in number of bits per second, default is 115200
   */
  Q_PROPERTY(
      int baudRate READ baudRate WRITE setBaudRate NOTIFY baudRateChanged)

  /** @brief Number of bits per word, must be between 5 and 8, default is 8 */
  Q_PROPERTY(
      int dataBits READ dataBits WRITE setDataBits NOTIFY dataBitsChanged)

  /** @brief Flow control, default is no flow control */
  Q_PROPERTY(FlowControl flowControl READ flowControl WRITE setFlowControl
                 NOTIFY flowControlChanged)

  /** @brief Number of parity bits, default is zero */
  Q_PROPERTY(Parity parity READ parity WRITE setParity NOTIFY parityChanged)

  /** @brief Number of stop bits, default is one */
  Q_PROPERTY(
      StopBits stopBits READ stopBits WRITE setStopBits NOTIFY stopBitsChanged)

public:
  enum FlowControl {
    NoFlowControl = QSerialPort::NoFlowControl,
    HardwareControl = QSerialPort::HardwareControl,
    SoftwareControl = QSerialPort::SoftwareControl
  };
  Q_ENUM(FlowControl)
  enum BaudRate {
    Baud2400 = 2400,
    Baud4800 = 4800,
    Baud9600 = 9600,
    Baud19200 = 19200,
    Baud38400 = 38400,
    Baud57600 = 57600,
    Baud115200 = 115200,
  };
  Q_ENUM(BaudRate)
  enum Parity {
    NoParity = QSerialPort::NoParity,
    EvenParity = QSerialPort::EvenParity,
    OddParity = QSerialPort::OddParity,
    SpaceParity = QSerialPort::SpaceParity,
    MarkParity = QSerialPort::MarkParity
  };
  Q_ENUM(Parity)
  enum DataBits {
    Data5 = QSerialPort::Data5,
    Data6 = QSerialPort::Data6,
    Data7 = QSerialPort::Data7,
    Data8 = QSerialPort::Data8
  };
  Q_ENUM(DataBits)
  enum StopBits {
    OneStop = QSerialPort::OneStop,
    OneAndHalfStop = QSerialPort::OneAndHalfStop,
    TwoStop = QSerialPort::TwoStop
  };
  Q_ENUM(StopBits)

  enum SerialPortError {
    NoError,
    DeviceNotFoundError,
    PermissionError,
    OpenError,
    ParityError,
    FramingError,
    BreakConditionError,
    WriteError,
    ReadError,
    ResourceError,
    UnsupportedOperationError,
    UnknownError,
    TimeoutError,
    NotOpenError
  };
  Q_ENUM(SerialPortError)

  /** @cond DO_NOT_DOCUMENT */
  SerialPort(QObject *parent = nullptr);
  virtual ~SerialPort();

  QString portName() { return portName_; }
  QString deviceName() { return deviceName_; }

  int baudRate() { return baudRate_; }

  int dataBits() { return dataBits_; }

  FlowControl flowControl() { return flowControl_; }

  Parity parity() { return parity_; }

  StopBits stopBits() { return stopBits_; }

  void setDeviceName(QString deviceName);
  void setPortName(QString portName);
  void setBaudRate(int baudRate);
  void setDataBits(int dataBits);
  void setFlowControl(FlowControl flowControl);
  void setParity(Parity parity);
  void setStopBits(StopBits stopBits);
  Q_INVOKABLE QString praseImei(QString topic);
  Q_INVOKABLE static QString getTs();
  /** @endcond */

signals:
  /** @cond DO_NOT_DOCUMENT */
  void deviceNameChanged();
  void portNameChanged();
  void baudRateChanged();
  void dataBitsChanged();
  void flowControlChanged();
  void parityChanged();
  void stopBitsChanged();
  /** @endcond */

  /**
   * @brief Emitted when any amount of data is received
   *
   * @param data Received data
   */
  void connectFailed(QString deviceName);
  void dataReceived(QByteArray data);
  // updater
  void downReplyTimeout();
  void ctlReplyTimeout();
  void packReplyTimeout();
  void downloadComplete();
  void progressChanged(float val);
  void addDownLog(QString msg);
  void downloadRunning();
  void downloadProgress(int index, int progress);
  void speedUpdate(int bytes);

public slots:

  /**
   * @brief Opens the port, all config must be done beforehand
   */
  virtual bool open();

  /**
   * @brief Closes the port
   */
  virtual void close();

  /**
   * @brief Sends the data over the port
   *
   * @param data Data to send
   */
  void sendData(QByteArray data);
  /**
   * @brief getPortsInfo
   * @return
   */
  QStringList getPortsInfo();
  virtual bool getStatus();
  static QString stringToHex(QByteArray msg);
  void onConnectionError(QSerialPort::SerialPortError error);
  QString error();
  void startUpdate(QByteArray fw);

private slots:
  void readyRead();
  void speedCntTimeout();

protected:
  QString deviceName_;
  QString portName_;
  int baudRate_;
  int dataBits_;
  FlowControl flowControl_;
  Parity parity_;
  StopBits stopBits_;

private:
  QSerialPort serialPort;
  Updater *updater;
  int m_speed;
};

//} // namespace qmlserialport
